Unity3D Building Damage Tutorial- Part IX - Branches
I decided to try burning some more complex objects, than cylinders and cubes. From Asset store, I've downloaded simple and free Arabian building, seen one the left picture, and than started fire from Alias number . Well, as you can see in the picture on right side of the screen, not everyting went as expected. Why? Becouse aparently mesh was made from several non-connected object. Thus, fire was not able to spread properly. In this part of tutorial, I'll try to overcome this problem. Branches In tutorial and code I'll use something named branch. Well, in my mind branch will mean a part of mesh not connected to other partsIn picture top right every wooden pole and top branches. We will assign Aliases to branches, but there will be no new class used. This partition will be much teoretical. Plan So what we have to do: #Divide the mesh to several branches, by assigning a branch number to every Alias. #Find inter-branch connections (later known as I-BC) #Tell specific Aliases that there are taking part in I-BC and thus should spread fire by them. FindAliasesBranches method public MeshManager (ref MeshFilter meshFilter){//constructor Initialise(); ParentPosition = meshFilter.transform.position; filter = meshFilter; mesh = meshFilter.mesh; ManageVerticles(mesh.vertices); ManageTriangles(mesh.triangles); CalcualateMinimumHeight(); CalculateNormals(); FindAliasesBranches(); MakeMeshThick(); } As you see, the function is called in the constructor prior to MakeMeshThick. We will not use any twins. private void FindAliasesBranches(){ AliasToBranchArray = new intAliases.Count; for(int i=0; i 0){//only one branch, so we dont need to look for connections for(int i=0; i FinalConnections = FindNumberOfInterBranchConnections(i, 2); foreach(Vector2 vec in FinalConnections){ Aliases(int)vec.x.AddLinkFromOtherBranch((int)vec.y); Aliases(int)vec.y.AddLinkFromOtherBranch((int)vec.x); } } } } Okay, first lets look at AliasToBranchArray (leter known as ATBA). It serves similar purpose to VTAA. The lenght of this array is number of original aliases (not twins). Than we will set the number of branch to every Alias for(int i=0; i AliasToBranchArrayi = -1; } Well, as it was stated above, just initialising with -1s. foreach(Verticle vec in Aliases){//Give AliasToBranchArray values if(AliasToBranchArrayvec.number -1){//this alias is not yet set to any branch AliasToBranchArrayvec.number = CurrentBranchNumber; GetConnectedAliases(vec.number, CurrentBranchNumber); CurrentBranchNumber++; } } Okay, this is moderatly intresting. In this loop we will be checking in which branch every Alias will be. if(AliasToBranchArrayvec.number -1){//this alias is not yet set to any branch Well, every time the loop will go, there will be more than one Alias assigned (later bout that in a second). Anyway, when code will go to this condition in one of further loops, this ATBA cell may be arleady assigned, so there is no point is counting some things again. GetConnectedAliases method private void GetConnectedAliases(int number, int CurrentBranchNumber){ List LinkedAliases = Aliasesnumber.LinkedAliases; foreach(int i in LinkedAliases){ if(AliasToBranchArrayi -1){ AliasToBranchArrayi = CurrentBranchNumber; GetConnectedAliases(i, ref AliasToBranchArray, CurrentBranchNumber); } } } Here is intresting code. This method has an aim of partially-filling the ATBA array. As we know branch is a group of Aliases, which have direct or indirect connection. Becouse of that, all LinkedAlias of alias being in branch 0, are in branch 0 too. In 6th line here we are calling current method for all LinkedAlias (as Alias linked to Alias linked ... to original alias are still in the same branch). This may seem to result in infinite function loop and stack overflow. But we have a if(AliasToBranchArrayi -1){ condition here. After some time data of branches (in ATBA array) of all Aliases in branch will be set, so functions will end. Back to FindAliasesBranches method if(CurrentBranchNumber > 0){//only one branch, so we dont need to look for connections for(int i=0; i FinalConnections = FindNumberOfInterBranchConnections(i, 2); foreach(Vector2 vec in FinalConnections){ Aliases(int)vec.x.AddLinkFromOtherBranch((int)vec.y); Aliases(int)vec.y.AddLinkFromOtherBranch((int)vec.x); } } } Okay, in this part of code we will find a small number (2 here) of closest connections in each Branch, and than send info of Aliases taking part in close connections that they are taking part in this connection, and effectively that they spread fire using them. The first thing is done is FindNumberOfInterBranchConnections method. FindNumberOfInterBranchConnections private List FindNumberOfInterBranchConnections(int BranchNumber, int HowManyConnections){//it searches for This branch Connections (Aliases close to themselves). One Alias can make only One connection! List ConnectionsList = new List(); //Vector2.x is the Alais in our branch Vector2.y is the closest Alias in other branch foreach(Verticle vec in Aliases){//here we are trying to get list of closest inter-branch connections of every Alias in breanch if(AliasToBranchArrayvec.number BranchNumber){ ConnectionsList.Add ( new Vector2(vec.number, FindCloseAliasNotInTheSameBranch(vec.number, BranchNumber)) ); } } List FinalConnections = FindNumberOfClosestConnections(ConnectionsList, 2); return FinalConnections; } Method does two things. #Make a list of pairs between each Alias in branch and an Alias that is closest to this Alias, but is in other branch (4th line) #Finds a given number (2 here) of pairs which distance between is smallest (8th line) And then returns this two pairs. FindCloseAliasNotInTheSameBranch method private int FindCloseAliasNotInTheSameBranch(int number, int BranchNumber){//finds and returns the Alias closest to Alias of given number, not being part of branch int CurrentClosestAlias = -1; float Distance = 1000f; //Just very big number foreach(Verticle vec in Aliases){ if(vec.number!=number){//verticle is not the being checked verticle if(AliasToBranchArrayvec.number!=BranchNumber){//not from the same branch if(Vector3.Distance(Aliasesnumber.positionRelative, vec.positionRelative) < Distance){ CurrentClosestAlias = vec.number; Distance = Vector3.Distance(Aliasesnumber.positionRelative, vec.positionRelative); } } } } return CurrentClosestAlias; } Well, just check every distance between Alias of given number and all Aliases in branch other than given. Than returns the number of Alias that makes the closest pair. FindNumberOfClosestConnections method private List FindNumberOfClosestConnections(List InList, int NumberOfClosestConnections){ Dictionary DistanceDictionary = new Dictionary(); //Key is the pair, value is the distance between foreach(Vector2 pair in InList){//lets fill the distionary with distances between each pair //Debug.Log("pair x is"+pair.x+" and y is "+pair.y); float Distance = Vector3.Distance(Aliases(int)pair.x.positionRelative, Aliases(int)pair.y.positionRelative); DistanceDictionary.Add (pair, Distance); } Dictionary ClosestConnections = FindANumberOfSmallestElementsInDictionary(DistanceDictionary, NumberOfClosestConnections); List OutList = new List(); foreach(KeyValuePair pair in ClosestConnections){ OutList.Add (pair.Key); } return OutList; } Well, this may seem complicated. At the begining we are making a Dictionary, which will store a pair of Aliases (making close connection) as key and float distance as value. The aim of that is to count all distances, and than find a given number (second parameter) of shortest ones. Finding these is done in FindANumberOfSmallestElementsInDictionary method. Than we have just a simple translation from Dictionary to a list (with losing of distance data). FindANumberOFSmallestElementsInDictionary method private Dictionary FindANumberOfSmallestElementsInDictionary(Dictionary InDictionary, int NumberOFSmallest){//returns a given number of pairs with smallest values; if(NumberOFSmallest>InDictionary.Count){ Debug.Log("Well, the count of given list is smaller than the amount of smallest things you want. That is wrong"); }; Dictionary OutDictionary = new Dictionary(); for(int i=0; i pair in InDictionary){ foreach(KeyValuePair pair2 in OutDictionary){ if(pair2.Value> pair.Value){ OutDictionary.Remove(pair2.Key); OutDictionary.Add (pair.Key, pair.Value); break; } } } return OutDictionary; } May look difficult, but is not so complicated. First, you may notice that i am adding some strange data to OutDictionary. I do this, to be able to use foreach statement on OutDictionary that will make the loop the number of times given in second function parameter. Later, there is just somehow tricky finding and putting to Dictionary pairs with smallest values. Back to FindAliasesBranches foreach(Vector2 vec in FinalConnections){ Aliases(int)vec.x.AddLinkFromOtherBranch((int)vec.y); Aliases(int)vec.y.AddLinkFromOtherBranch((int)vec.x); } Okay, and here we send info to Aliases about them being in pairs. This is last thing done in method. Verticle class ... ... ... ... ... ... ... public List LinksToOtherBranches; ... ... ... ... if(state VerticleState.Burning){ InflictDamage(); TryToFireLinkedAliases(); TryToFireLinkedAliasesFromOtherBranches(); } ... ... ... ... ... public void AddLinkFromOtherBranch(int i){ if(!LinksToOtherBranches.Contains(i)){ LinksToOtherBranches.Add(i); } //DebEnlightenThisAlias(); //DebEnlightenAAlias(i, Color.magenta); } ... ... ... private void TryToFireLinkedAliasesFromOtherBranches(){ foreach(int i in LinksToOtherBranches){ OwnerManager.Aliasesi.StartFire(); } } ... ... ... private void Destroy(){ //if(number 100){Debug.Log("Linked Alias is "+LinkedAliases1);} OwnerManager.MeshWasChanged = true; float time = 0f; time = Time.realtimeSinceStartup; for(int i = 0; i